package com.dou.cloud.bridge.redis.aspect;

import com.dou.cloud.bridge.redis.annotation.RLock;
import com.dou.cloud.bridge.redis.handler.RedissonFactory;
import com.due.basic.tookit.exception.LogicException;
import com.due.basic.tookit.function.DueVoidProducer;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;

import static com.due.basic.tookit.constant.RedisConstant.*;

/**
 * @Author daizl
 * @Date 2024 06 19 10 43
 **/
@Aspect
@Component
public class LockAspect {

    /**
     * 用于获取方法参数定义名字.
     */
    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    /**
     * 用于SpEL表达式解析.
     */
    private static final SpelExpressionParser parser = new SpelExpressionParser();


    @Resource
    private RedissonFactory redissonFactory;

    /**
     * 分布式锁切面
     *
     * @param joinPoint 切点
     * @return 返回值
     */
    @Around("@annotation(com.dou.cloud.bridge.redis.annotation.RLock) && execution(* *(..))")
    public Object lockOperation(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取目标类
        Object target = joinPoint.getThis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        RLock lockAnnotation = signature.getMethod().getAnnotation(RLock.class);
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
        // 获取方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 可解决代理对象问题
        Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(target, targetMethod, args, parameterNameDiscoverer);
        String keyPrefix = R_LOCK + lockAnnotation.keyPrefix();
        String annotationKey = lockAnnotation.key();
        String key = "";
        if (StringUtils.isBlank(annotationKey)) {
            // todo 获取用户ID
//            Long userId = UserSer.getUser(true).getUserId();
//            key = keyPrefix + userId;

        } else {
            String expValue = getExpValue(context, annotationKey);
            key = keyPrefix + expValue;
        }
        // Proceed with the original method execution
        AtomicReference<Object> result = new AtomicReference<>();
        org.redisson.api.RLock rlock = null;
        try {
            rlock = redissonFactory.getStrategy(lockAnnotation.lockType()).tryLock(() -> {
                try {
                    result.set(joinPoint.proceed());
                    return result;
                } catch (Throwable e) {
                    throw new LogicException(String.valueOf(e));
                }
            }, key, lockAnnotation.waitTime(), lockAnnotation.timeOut(), lockAnnotation.timeUnit());
        } catch (Exception e) {
            // Handle lock exception
            throw new LogicException(FAILED_TO_ACQUIRE_LOCK + e.getMessage());
        } finally {
            if (lockAnnotation.isReleaseLock()) {
                if (rlock != null && rlock.isHeldByCurrentThread())
                    rlock.unlock();
            }
        }
        return result.get();
    }


    private String getExpValue(MethodBasedEvaluationContext context, String expr) {
        Expression expression = parser.parseExpression(expr);
        return (String) expression.getValue(context);
    }

}
